package com.xiao.mall.foundation.lock;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Component("SimpleRedisLock")
@Slf4j
public class SimpleRedisLock {

    @Autowired
    private StringRedisTemplate redisTemplate;

    public static final long EXPIRE = 5000;

    public CloseableLock tryLock(String key) {
        String lockValue = UUID.randomUUID().toString();
        CloseableLock lock = new CloseableLock(this, key, lockValue);
        Long start = System.currentTimeMillis();

        while (true) {
            // 检测是否超时

            // 执行set命令
            Boolean absent = redisTemplate.opsForValue().setIfAbsent(key, lockValue, EXPIRE, TimeUnit.MILLISECONDS);// 毫秒
            // 是否成功获取锁
            if (absent) {
                return lock;
            }

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.error("sleep error: {}", e.getMessage());
            }

            long end = System.currentTimeMillis() - start;
            if (end >= EXPIRE) {
                // TODO: 高并发下会有问题
                // 锁过期，强行解锁。
                redisTemplate.delete(key);
                redisTemplate.opsForValue().setIfAbsent(key, lockValue, EXPIRE, TimeUnit.MILLISECONDS);// 毫秒

                return lock;
            }
        }
    }

    public void unlock(String key, String value) {
        // TODO: 高并发下会出现误删。
        if (value.equals(redisTemplate.opsForValue().get(key))) {
            redisTemplate.delete(key);
        }

        /*使用LUA脚本保证get-del操作线程安全。*/
//        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end";
//        Long execute = redisTemplate.execute((RedisConnection conn) -> conn.eval(script.getBytes(),
//                ReturnType.INTEGER, 1, key.getBytes()));
    }
}
